﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Reflection;
using System.Linq.Expressions;
using TDT.Land.Utilities;
using System.Web.Mvc;

namespace TDT.Land.Entity
{
    public class EntityQuery : DataEntities
    {
        public bool _AddEntity<T>(T Insert, string UserInfo) where T : EntityObject
        {
            return _AddEntity<T>(new List<T>() { Insert }, UserInfo);
        }
        /// <summary>
        /// Add List Into DB
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="lstInsert">list Insert to DB</param>
        public bool _AddEntity<T>(List<T> lstInsert,string UserInfo) where T : EntityObject
        {
            EntityQuery entity = new EntityQuery();
            Type typeParameterType = typeof(T);
            PrepareInsert(lstInsert, UserInfo);
            foreach (var item in lstInsert)
            {
                entity.AddObject(typeParameterType.Name, item);
            }
            int result = entity.SaveChanges();
            ReleaseContext(entity);
            if (result == 1)
                return true;
            else
                return false;

        }


        public bool _UpdateEntity<T>(T Update, string UserInfo) where T : EntityObject
        {
            return _UpdateEntity(Update, new List<string>(), UserInfo);
        }

        /// <summary>
        /// Update dữ liệu
        /// </summary>
        /// <typeparam name="T">Kiểu dữ liệu nằm trong entity</typeparam>
        /// <param name="Update">đối tượng cần được update</param>
        /// <param name="lstFieldNonUpdate">ds những cột không cần update</param>
        /// <returns></returns>
        public bool _UpdateEntity<T>(T Update, List<string> lstFieldNonUpdate, string UserInfo) where T : EntityObject
        {
            return _UpdateEntity(new List<T>() { Update }, lstFieldNonUpdate, UserInfo);
        }

        public bool _UpdateEntity<T>(List<T> lstUpdate, string UserInfo) where T : EntityObject
        {
            return _UpdateEntity(lstUpdate, new List<string>(), UserInfo);
        }

        /// <summary>
        /// Update List to DB
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="lstUpdate">list data update to DB</param>
        public bool _UpdateEntity<T>(List<T> lstUpdate, List<string> lstFieldNonUpdate, string UserInfo) where T : EntityObject
        {
            EntityQuery entity = new EntityQuery();
            IObjectSet<T> _objectSet = entity.CreateObjectSet<T>();
            Type typeParameterType = typeof(T);
            PrepareUpdate(lstUpdate, UserInfo);
            foreach (var item in lstUpdate)
            {
                //_objectSet.Attach(item);
                try
                {
                    entity.AddObject(typeParameterType.Name, item);
                }
                catch
                {
                    entity.Attach(item);
                }
                entity.ObjectStateManager.ChangeObjectState(item, System.Data.EntityState.Modified);
                foreach (ObjectStateEntry entry in entity.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified))
                {
                    var properties = entry.GetModifiedProperties().ToList<string>();
                    entry.ChangeState(System.Data.EntityState.Unchanged);
                    foreach (var propertyName in properties)
                    {
                        if (propertyName != "UserCreate" && propertyName != "DateCreate" && !lstFieldNonUpdate.Any(m => m == propertyName))
                            entry.SetModifiedProperty(propertyName);
                    }
                }
            }

            int result = entity.SaveChanges();
            ReleaseContext(entity);
            if (result >= 1)
                return true;
            else
                return false;
        }


        /// <summary>
        /// Lấy tất cả dữ liệu trong DB
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public List<T> _GetEntityList<T>() where T : EntityObject
        {
            return this._GetEntityList<T>(null);
        }


        /// <summary>
        /// Get List From DB
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate">Query to get list</param>
        /// <returns></returns>
        public List<T> _GetEntityList<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T : EntityObject
        {

            EntityQuery entity = new EntityQuery();
            IObjectSet<T> _objectSet = entity.CreateObjectSet<T>();
            List<T> result = new List<T>();

            IQueryable<T> QueryPrepared = PrepareQueryToGet(_objectSet.AsQueryable());


            //System.Linq.Expressions.Expression<Func<T, bool>> predicateNotDelete = PrepareToGetListNotDelete<T>(predicate);
            if (predicate != null)
            {
                result = QueryPrepared.Where(predicate).ToList();
                //result = _objectSet.AsQueryable().Where(predicate).ToList();
            }
            else
            {
                result = QueryPrepared.ToList();
            }
            ReleaseContext(entity);
            return result;
        }


        public bool _DeleteEntity<T>(T Delete, string UserInfo) where T : EntityObject
        {
            return _DeleteEntity<T>(new List<T>() { Delete }, UserInfo);
        }
        /// <summary>
        /// delete list : update with status isdelete = true
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="lstDelete"></param>
        public bool _DeleteEntity<T>(List<T> lstDelete, string UserInfo) where T : EntityObject
        {
            PrepareDelete(lstDelete);
            return _UpdateEntity(lstDelete, UserInfo);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">Class of entity</typeparam>
        /// <param name="predicate">Query to find Value</param>
        /// <param name="selectQuery">Get some colume in db to select value</param>
        /// <returns></returns>
        public IQueryable<T> _GetMultiColume<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate, out EntityQuery entityResult) where T : EntityObject
        {
            //EntityQuery entity = new EntityQuery();
            //IObjectSet<Per_UserInfo> _objectSet = entity.CreateObjectSet<Per_UserInfo>();
            //IEnumerable query = _objectSet.Where(m => m.ID != Guid.Empty).Select(m => m.Email).ToList();

            EntityQuery entity = new EntityQuery();
            IObjectSet<T> _objectSet = entity.CreateObjectSet<T>();
            IQueryable<T> QueryPrepared = PrepareQueryToGet(_objectSet.AsQueryable());
            IQueryable<T> result = null;
            if (predicate == null)
            {
                result = QueryPrepared.AsQueryable<T>();
            }
            else
            {
                result = QueryPrepared.Where(predicate);
            }
            entityResult = entity;
            return result;
        }


        public void ReleaseContext(EntityQuery Entity)
        {
            //Entity = new EntityQuery();
            Entity.Dispose();
            Entity = null;
        }
        private void PrepareInsert<T>(List<T> lstInsert, string UserInfo) where T : EntityObject
        {
            DateTime Now = DateTime.Now;
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties();
            PropertyInfo DateCreateInfo = properties.Where(m => m.Name == "DateCreate").FirstOrDefault();
            PropertyInfo UserCreateInfo = properties.Where(m => m.Name == "UserCreate").FirstOrDefault();

            if (DateCreateInfo != null && UserCreateInfo != null)
            {
                foreach (var item in lstInsert)
                {
                    DateCreateInfo.SetValue(item, Now, null);
                    UserCreateInfo.SetValue(item, UserInfo, null);
                }
            }


        }
        private void PrepareUpdate<T>(List<T> lstUpdate,string UserInfo) where T : EntityObject
        {
            DateTime Now = DateTime.Now;
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties();
            PropertyInfo DateUpdateInfo = properties.Where(m => m.Name == "DateUpdate").FirstOrDefault();
            PropertyInfo UserUpdateInfo = properties.Where(m => m.Name == "UserUpdate").FirstOrDefault();
            //string fsdf = UserNameFull;

            if (DateUpdateInfo != null && UserUpdateInfo != null)
            {
                foreach (var item in lstUpdate)
                {
                    DateUpdateInfo.SetValue(item, Now, null);
                    UserUpdateInfo.SetValue(item, UserInfo, null);
                }
            }

        }
        private void PrepareDelete<T>(List<T> lstDelete) where T : EntityObject
        {
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties();
            PropertyInfo IsDeleteInfo = properties.Where(m => m.Name == "IsDelete").FirstOrDefault();

            if (IsDeleteInfo != null)
            {
                foreach (var item in lstDelete)
                {
                    IsDeleteInfo.SetValue(item, true, null);
                }
            }

        }
        private IQueryable<T> PrepareQueryToGet<T>(IQueryable<T> iQueryable) where T : EntityObject
        {
            var tagMember = typeof(T).GetProperty("IsDelete");
            var eParam = Expression.Parameter(typeof(T), "e");

            var right1 = Expression.Constant(null, typeof(bool?));
            var predicate1 =
                Expression.Lambda<Func<T, bool>>(
                    Expression.Equal(
                        Expression.Property(eParam, tagMember),
                      right1
                        
                    ),
                    eParam);
            
            var right2 = Expression.Constant(false, typeof(bool?));
            var predicate2 = Expression.Lambda<Func<T, bool>>(
                    Expression.Equal(
                        Expression.Property(eParam, tagMember),
                        right2
                    ),
                    eParam);
            var right3 = Expression.OrElse(predicate1.Body, predicate2.Body);
            var lambda = Expression.Lambda<Func<T, bool>>(right3, predicate1.Parameters[0]);
            return iQueryable.Where(lambda);
        }
        


    }
    public class IEntity
    {
        private bool? _IsDelete;
        public bool? IsDelete
        {
            get
            {
                return _IsDelete;
            }
            set
            {
                _IsDelete = value;
            }
        }
    }

}
